#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>

#define COMMAND_SIZE 1024
#define FORMAT "[%s@%s %s]# "

//1.命令行参数表
#define MAXARGC 128
char*g_argv[MAXARGC];
int g_argc=0;

//2.环境变量表 
#define MAX_ENVS 100
char*g_env[MAX_ENVS];
int g_envs=0;

char cwd[1024];
char cwdenv[1024];
//last exit code 
int lastcode=0;

const char*GetUserName()
{
  return getenv("USER");
}
const char*GetHostName()
{
  return getenv("HOSTNAME");
}
const char*GetPwd()
{
  //return getenv("PWD");
  const char*pwd=getcwd(cwd,sizeof(cwd));
  if(pwd!=NULL)
  {
    snprintf(cwdenv,sizeof(cwdenv),"PWD=%s",cwd);
    putenv(cwdenv);
  }
  return pwd==NULL?"None":pwd;
}
const char*GetHome()
{
  return getenv("HOME") == NULL ? "" : getenv("HOME");
}
std::string DirName(const char*pwd)
{
#define SLASH "/"
  std::string dir=pwd;
  if(dir==SLASH) return SLASH;
  auto pos=dir.rfind(SLASH);
  if(pos==std::string::npos) return "BUG?";
  return dir.substr(pos+1);
}

void InitEnv()
{
  extern char**environ;
  memset(g_env,0,sizeof(g_env));
  g_envs=0;
  
  for(int i=0;environ[i];i++)
  {
    g_env[i]=(char*)malloc(strlen(environ[i])+1);
    strcpy(g_env[i],environ[i]);
    g_envs++;
  }
  g_env[g_envs++]=(char*)"HAHA=for_test";
  g_env[g_envs]=NULL;
  for(int i=0;g_env[i];i++)
  {
    putenv(g_env[i]);
  }
  environ=g_env;
}
void MakeCommandLine(char cmd_prompt[],int size)
{
  snprintf(cmd_prompt,size,FORMAT,GetUserName(),GetHostName(),DirName(GetPwd()).c_str());
  //snprintf(cmd_prompt,size,FORMAT,GetUserName(),GetHostName(),GetPwd());
}
void PrintCommandPrompt()
{
  char prompt[COMMAND_SIZE];
  MakeCommandLine(prompt,sizeof(prompt));
  printf("%s",prompt);
  fflush(stdout);
  
}

bool GetCommandLine(char*out,int size)
{
  char*c=fgets(out,size,stdin);
  if(c==NULL) return false;
  out[strlen(out)-1]=0;//清理\n
  if(strlen(out)==0) return false;
  return true;
}

bool CommandParse(char*commandline)
{
#define SPE " "
  g_argc=0;
  g_argv[g_argc++]=strtok(commandline,SPE);
  while((bool)(g_argv[g_argc++]=strtok(nullptr,SPE)));
  g_argc--;
  return g_argc>0?true:false;
}
void PrintArgv()
{
  for(int i=0;g_argv[i];i++)
  {
    printf("argv[%d]->%s\n",i,g_argv[i]);
  }
  printf("argc: %d\n",g_argc);
}
bool Cd()
{
  if(g_argc==1)
  {
    std::string home=GetHome();
    if(home.empty()) return true;
    chdir(home.c_str());
  }
  else 
  {
    std::string where=g_argv[1];

    if(where=="-")
    {

    }
    else if(where=="~")
    {

    }
    else 
    {
      chdir(where.c_str());
    }
  }
  return true;
}

void Echo()
{
  if(g_argc==2)
  {
    std::string opt=g_argv[1];
    if(opt=="$?")
    {
      std::cout << lastcode << std::endl;
      lastcode=0;;
    }
    else if(opt[0]=='$')
    {
      std::string env_name=opt.substr(1);
      const char*env_value=getenv(env_name.c_str());
      if(env_value)
      {
        std::cout<< env_value << std::endl;
      }
    }
    else 
    {
      std::cout<< opt <<std::endl;
    }
  }
}
bool CheckAndExecBuiltin()
{
  std::string cmd=g_argv[0];
  if(cmd=="cd")
  {
    Cd();
    return true;
  }
  else if(cmd=="echo")
  {
    Echo();
    return true;
  }

  return false;
}
int Execute()
{
  pid_t id=fork();
  if(id==0)
  {
    execvp(g_argv[0],g_argv);
    exit(1);
  }
  int status=0;
  pid_t rid=waitpid(id,&status,0);
  if(rid>0)
  {
    lastcode=WEXITSTATUS(status);
  }
  return 0;
}
int main()
{
  //shell 启动的时候，从系统中获取环境变量
  //我们的环境变量应从父shell统一来
  InitEnv();
  while(true)
  {
    //1.输出命令行提示符
    PrintCommandPrompt();

    //2.获取用户输入的命令
    char commandline[COMMAND_SIZE];
    if(!GetCommandLine(commandline,sizeof(commandline))) 
      continue;
    //3.命令行分析
    if(!CommandParse(commandline))
      continue;
    // PrintArgv();
    //4.检测并处理内键命令
    if(CheckAndExecBuiltin())
      continue;
    //5.执行命令
    Execute();
  }
  return 0;
}
